package com.meizi.codegenerate.generate.clien.application.service;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.meizi.codegenerate.constant.JavaClassNameConstant;
import com.meizi.codegenerate.constant.MethodNameConstant;
import com.meizi.codegenerate.constant.TableNameConstant;
import com.meizi.codegenerate.entity.ProjectPositionEntity;
import com.meizi.codegenerate.entity.TablesEntity;
import com.meizi.codegenerate.generate.clien.application.service.client.FeignGenerate;
import com.meizi.codegenerate.generate.clien.domain.repository.IRepositoryGenerate;
import com.meizi.codegenerate.generate.server.application.service.impl.ServiceImplGenerate;
import com.meizi.codegenerate.packagegenerate.PackageGenerate;
import com.meizi.codegenerate.tool.page.PageData;
import com.meizi.codegenerate.utils.AnnotationUtils;
import com.meizi.codegenerate.utils.AttributeConvertUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author pc
 * @date 2024/1/4 - 15:00 - 星期四
 * @project lambda
 * @package com.meizi.codegenerate.generate.clien.application.service
 * @by GGbomd猪猪侠
 */
@Slf4j
public class ServiceGenerate {

    /**
     * 创建 serviceGenerate interface
     * @throws IOException
     */
    public static void createService (String tableName, TablesEntity tablesComment,
                                      ProjectPositionEntity projectPosition, String respBodyClassName) throws IOException {

        // 在相应的路径下创建对应的文件
        String serviceUrl = PackageGenerate.obtainServiceUrl(projectPosition);

        //获取service java类名
        String className = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, true)
                + JavaClassNameConstant.SERVICE;
        // 获取完成的文件路径创建文件
        String fullPath = serviceUrl + className + TableNameConstant.JAVA;
        log.info("创建 application 下service路径 fullPath = {}", fullPath);

        CompilationUnit compilationUnit = new CompilationUnit();
        // 添加package
        compilationUnit.setPackageDeclaration(PackageGenerate.servicePackage(projectPosition));

        compilationUnit.addImport("io.swagger.annotations.ApiOperation");
        compilationUnit.addImport("org.springframework.web.bind.annotation.PostMapping");
        compilationUnit.addImport("org.springframework.web.bind.annotation.RequestBody");
        compilationUnit.addImport("org.springframework.web.bind.annotation.RequestMapping");
        compilationUnit.addImport("org.springframework.web.bind.annotation.RequestMethod");

        // 添加接口文件
        ClassOrInterfaceDeclaration declaration = compilationUnit.addInterface(className).setPublic(true);
        //添加类级别注释
        declaration.setJavadocComment(tablesComment.getTableComment() + " 服务层接口");

        // 获取传参
        String paramQueryBody = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, true)
                + JavaClassNameConstant.PAGE_PARAM_QUERY_BODY;
        String path = getPath(tableName, "/" + MethodNameConstant.SERVICE_GET_LIST);
        //添加抽象方法 getList
        MethodDeclaration getListMethod = methodGetList(MethodNameConstant.SERVICE_GET_LIST, respBodyClassName, paramQueryBody, path);
        declaration.addMember(getListMethod);

        String getPageList = getPath(tableName, "/" + MethodNameConstant.SERVICE_GET_PAGE_LIST);
        //添加抽象方法 getPageList
        MethodDeclaration getPageListMethod = methodGetPageList(MethodNameConstant.SERVICE_GET_PAGE_LIST, respBodyClassName, paramQueryBody, getPageList);
        declaration.addMember(getPageListMethod);

        String getCount = getPath(tableName, "/" + MethodNameConstant.SERVICE_GET_COUNT);
        //添加抽象方法 getCount
        MethodDeclaration getCountMethod = methodGetCount(MethodNameConstant.SERVICE_GET_COUNT, paramQueryBody, getCount);
        declaration.addMember(getCountMethod);

        String exists = getPath(tableName, "/" + MethodNameConstant.SERVICE_EXISTS);
        //添加抽象方法 exists
        MethodDeclaration existsMethod = methodGetexists(MethodNameConstant.SERVICE_EXISTS, paramQueryBody, exists);
        declaration.addMember(existsMethod);


        String detailByIdReq = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, true)
                + JavaClassNameConstant.REQUEST_BODY_ById;
        String getDetailById = getPath(tableName, "/" + MethodNameConstant.SERVICE_GET_DETAIL_BYID);
        //添加抽象方法 getDetailById
        MethodDeclaration getDetailByIdMethod = methodGetDetailById(MethodNameConstant.SERVICE_GET_DETAIL_BYID, respBodyClassName, detailByIdReq, getDetailById);
        declaration.addMember(getDetailByIdMethod);

        String getIdList = getPath(tableName, "/" + MethodNameConstant.SERVICE_GET_ID_LIST);
        //添加抽象方法 getIdList
        MethodDeclaration getIdListMethod = methodGetIdListMethod(MethodNameConstant.SERVICE_GET_ID_LIST, paramQueryBody, getIdList);
        declaration.addMember(getIdListMethod);

        // 获取传参类
        String reqName = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, true)
                + JavaClassNameConstant.REQUEST_BODY;
        String create = getPath(tableName, "/" + MethodNameConstant.SERVICE_CREATE);
        //添加抽象方法 create
        MethodDeclaration createMethod = methodCreate(MethodNameConstant.SERVICE_CREATE, reqName, create);
        declaration.addMember(createMethod);

        /*String createBatch = getPath(tableName, "/createBatch");
        //添加抽象方法 createBatch
        MethodDeclaration createBatchMethod = methodCreateBatch("createBatch", reqName, createBatch);
        declaration.addMember(createBatchMethod);*/

        String updateModel = getPath(tableName, "/" + MethodNameConstant.SERVICE_UPDATE_MODEL);
        //添加抽象方法 updateModel
        MethodDeclaration updateModelMethod = methodUpdateModel(MethodNameConstant.SERVICE_UPDATE_MODEL, reqName, updateModel);
        declaration.addMember(updateModelMethod);

        String saveModel = getPath(tableName, "/" + MethodNameConstant.SERVICE_SAVE_MODEL);
        //添加抽象方法 saveModel
        MethodDeclaration saveModelMethod = methodSaveModel(MethodNameConstant.SERVICE_SAVE_MODEL, reqName, saveModel);
        declaration.addMember(saveModelMethod);

        String delete = getPath(tableName, "/" + MethodNameConstant.SERVICE_DELETE);
        //添加抽象方法 delete
        MethodDeclaration deleteMethod = methodDelete(MethodNameConstant.SERVICE_DELETE, paramQueryBody, delete);
        declaration.addMember(deleteMethod);

        // 获取传参类
        String deleteByIdReqName = AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, true)
                + JavaClassNameConstant.REQUEST_DELETEBY_BYID;
        String deleteById = getPath(tableName, "/" + MethodNameConstant.SERVICE_DELETE_BYID);
        //添加抽象方法 deleteById
        MethodDeclaration deleteByIdMethod = methodDeleteById(MethodNameConstant.SERVICE_DELETE_BYID, deleteByIdReqName, deleteById);
        declaration.addMember(deleteByIdMethod);

        log.info("生成service接口 compilationUnit = {}", compilationUnit.toString());

        /**
         * 把字符串写入文件
         * 参数1：需要写入的文件，如果文件不存在，将自动创建。  参数2：需要写入的内容
         * 参数3：编码格式     参数4：是否为追加模式（ ture: 追加模式，把字符串追加到原内容后面）
         */
        FileUtils.writeStringToFile(new File(fullPath), compilationUnit.toString(), "UTF-8", false);

        // 生成feign 远程调用
        FeignGenerate.createFeign(tableName, tablesComment, projectPosition, className);
        // 生成 仓储层接口
        IRepositoryGenerate.createIRepositoryGenerate(tableName, tablesComment, projectPosition, paramQueryBody);
        // 生成 service实现类接口
        ServiceImplGenerate.createIServiceImplGenerate(tableName, className, tablesComment, projectPosition, paramQueryBody, detailByIdReq, reqName, deleteByIdReqName, respBodyClassName);
    }

    private static MethodDeclaration methodDeleteById(String name, String reqName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法0
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, reqName), "reqBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodDelete(String name, String reqName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, reqName), "condition").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodSaveModel(String name, String reqName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, reqName), "reqBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodUpdateModel(String name, String reqName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, reqName), "reqBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodCreateBatch(String name, String reqName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, reqName), "reqBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodCreate(String name, String reqName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, reqName), "reqBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodGetIdListMethod(String name, String requestName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType mapType = new ClassOrInterfaceType(Long.class.getSimpleName());
        ClassOrInterfaceType listType = new ClassOrInterfaceType(List.class.getSimpleName());
        listType.setTypeArguments(mapType);

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, requestName), "queryBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodGetDetailById(String name, String respBodyClassName, String requestName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(respBodyClassName);

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, requestName), "queryBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodGetexists(String name, String requestName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Boolean.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, requestName), "queryBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodGetCount(String name, String requestName, String path) {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType listType = new ClassOrInterfaceType(Integer.class.getSimpleName());

        // 生成方法
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, requestName), "queryBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static String getPath(String tableName, String s) {
        return "/"+ AttributeConvertUtils.tableNameToJava
                (tableName, TableNameConstant.TABLE_PREFIX, false)
                + s;
    }

    private static MethodDeclaration methodGetPageList(String name, String respBodyClassName, String requestName, String path) {

        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType mapType = new ClassOrInterfaceType(respBodyClassName);
        //mapType.setTypeArguments(new ClassOrInterfaceType(String.class.getSimpleName()), new ClassOrInterfaceType(Object.class.getSimpleName()));
        ClassOrInterfaceType listType = new ClassOrInterfaceType(PageData.class.getSimpleName());
        listType.setTypeArguments(mapType);

        // 生成方法 GetPageList
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, requestName), "queryBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);

        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static MethodDeclaration methodGetList(String name, String respBodyClassName, String requestName, String path) {

        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        // 添加返回值类型
        ClassOrInterfaceType mapType = new ClassOrInterfaceType(respBodyClassName);
        //mapType.setTypeArguments(new ClassOrInterfaceType(String.class.getSimpleName()), new ClassOrInterfaceType(Object.class.getSimpleName()));
        ClassOrInterfaceType listType = new ClassOrInterfaceType(List.class.getSimpleName());
        listType.setTypeArguments(mapType);

        // 生成方法 getList
        map.put("value", name);
        MethodDeclaration method = new MethodDeclaration();
        method.setType(listType);
        method.setName(name);
        method.addParameter(new Parameter(new ClassOrInterfaceType(null, requestName), "queryBody").addAnnotation(RequestBody.class));
        method.addAnnotation(AnnotationUtils.setAnnotation(ApiOperation.class, new NodeList<>(),
                        map));
        clearMap(map);

        map.put("value", path);
        method.addAnnotation(AnnotationUtils.setAnnotation(PostMapping.class, new NodeList<>(), map));
        clearMap(map);


        // 去除方法体
        method.setBody(null);

        return method;
    }

    private static void clearMap(Map<String, Object> map) {
        map.clear();
    }
}
